package com.corpgovernment.common.requestprocess;

import java.time.LocalDateTime;
import java.util.Collections;
import java.util.Map;

import lombok.extern.slf4j.Slf4j;

/**
 * 基础请求处理器
 * 
 * @author dlz
 * @since 2024/3/20
 */
@Slf4j
public abstract class BaseRequestHandler<Req, Res> {

    public Res process(Req request) {
        Res response = null;
        try {
            // 初始化日志上下文
            LogContextUtil.initContext();
            LogContextUtil.setActionName(getActionName());
            LogContextUtil.setStartTime(LocalDateTime.now());

            // 请求参数合法性校验
            verify(request);

            // 处理请求
            response = doProcess(request);

        } catch (Exception e) {
            // 处理未知异常
            LogContextUtil.setServerException(e);
            throw e;
        } finally {
            // 响应日志埋点
            responseLogMetric(request, response);
            // 移除日志上下文
            LogContextUtil.remove();
        }
        return response;
    }

    /**
     * 校验请求,正常情况无需实现此方法，一般用spring注解校验
     * 
     * @param request 请求
     * @throws com.corpgovernment.common.common.CorpBusinessException 校验不通过抛出异常
     */
    protected void verify(Req request) {

    }

    /**
     * 记录响应日志埋点
     * 
     * @param request 请求
     * @param response 响应
     */
    private void responseLogMetric(Req request, Res response) {
        try {
            // 埋点处理
            doMetric(request, response);
        } catch (Throwable e) {
            log.error(getActionName() + "接口埋点失败", e);
        }
        try {
            // 日志处理
            doLog(request, response);
        } catch (Throwable e) {
            log.error(getActionName() + "接口记录日志失败", e);
        }
    }

    /**
     * 获取调用的接口名
     * 例如url是settlementDetail/adjustSettlementItem，这里就写settlementDetail.adjustSettlementItem
     */
    protected abstract String getActionName();

    /**
     * 处理请求
     *
     * @param request 请求
     * @return 响应
     */
    protected abstract Res doProcess(Req request);

    /**
     * 执行响应埋点
     * 
     * @param request 请求
     * @param response 响应
     */
    private void doMetric(Req request, Res response) {
        MetricUtil.build("request.handle." + getActionName())
            .tag("errorCode", LogContextUtil.getCorpBusinessExceptionCode())
            .tag("exceptionName", LogContextUtil.getServerExceptionName())
            .tag(getMetricTag(request, response))
            .recordByTimer(LogContextUtil.getDuration());
    }

    /**
     * 执行日志记录
     *
     * @param request 请求
     * @param response 响应
     */
    protected void doLog(Req request, Res response) {
        // 默认不记，正常情况aop会记日志
    }

    /**
     * 获得埋点tag
     */
    protected Map<String, String> getMetricTag(Req request, Res response) {
        return Collections.emptyMap();
    }
}
